package weka;

import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ArffLoader;
import weka.filters.Filter;
import weka.filters.supervised.instance.Resample;
import weka.filters.unsupervised.attribute.Remove;
import weka.filters.unsupervised.attribute.StringToNominal;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayesUpdateable;
import weka.classifiers.rules.DecisionTable;
import weka.classifiers.rules.JRip;
import weka.classifiers.trees.J48graft;
import weka.classifiers.trees.RandomForest;
import weka.classifiers.trees.RandomTree;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Random;


public class WekaTools {
	public String currentFile= new String();
	private DecimalFormat df = new DecimalFormat("#.###");
    public WekaTools() {
    	
    }
	public Instances readArff(String filename) throws IOException {
		BufferedReader reader;
		Instances data = null;
		currentFile = filename;
		try {
			reader = new BufferedReader(new FileReader(
					filename));
		
		data = new Instances(reader);
		reader.close();
		data.setClassIndex(data.numAttributes() - 1);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// setting class attribute
		
		return data;
	}
	
	public Instances filterMMS(Instances dataSet) {
		 String[] options = new String[2];
		 
		 Instances newData=null;
		 StringToNominal str2Nom = new StringToNominal();                         // new instance of filter
		 Remove remove = new Remove();
		 try {
			 options[0] = "-R";                                    // "range"
			 options[1] = "5,11";                                     // first attribute
			 //options[2] = "11";
			 str2Nom.setOptions(options);                           // set options
			 str2Nom.setInputFormat(dataSet);                          // inform filter about dataset **AFTER** setting options
			 newData = Filter.useFilter(dataSet, str2Nom);   // apply filter
			 options[0] = "-R";                                    // "range"
			 options[1] = "2,9";                                     // first attribute
			 //options[2] = "9";
			 remove.setOptions(options);                           // set options
			 remove.setInputFormat(newData);                          // inform filter about dataset **AFTER** setting options
			 newData = Filter.useFilter(newData, remove);   // apply filter 
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 return newData;
	}
	
	public boolean saveToArff(Instances dataSet, String filename) {
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
			 writer.write(dataSet.toString());
			 writer.flush();
			 writer.close();
			 return true;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}
	
	public String randomTree(Instances dataSet) {
		 
		 
		String result="";
		 String[] options = new String[6];
		 options[0] = "-K";
		 options[1] = "0";
		 options[2] = "-M";
		 options[3] = "1.0";
		 options[4] = "-S";
		 options[5] = "1";
		 RandomTree tree = new RandomTree();         // new instance of tree
		 try {
			tree.setOptions(options);     // set the options
			tree.buildClassifier(dataSet);
			Evaluation eval = new Evaluation(dataSet);
			eval.crossValidateModel(tree, dataSet, 5, new Random(1));
			result = tree.toString();
			result += eval.toSummaryString();
			result += eval.toClassDetailsString();
			result += eval.toMatrixString();
			System.out.println(eval.toMatrixString());
			System.out.println(genTable(eval,"RandomTree")); 
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		 return result;
		 
	}
	
	public String genTable(Evaluation eval,String legend) {
		String content= "";
		String filename2 = currentFile.replace(".arff", "RES.log");
		content = legend + " & " + df.format(eval.truePositiveRate(1)) + " & " + df.format(eval.falseNegativeRate(0)) + " & " + 
		(eval.numFalseNegatives(0) + eval.numFalseNegatives(1)) + "\\\\ \n";
		this.addToLog(filename2, content);
		return content;
		
	}
	
	public String randomForest(Instances dataSet) {
		 
		 
		String result="";
		 String[] options = new String[6];
		 options[0] = "-I";
		 options[1] = "10";
		 options[2] = "-K";
		 options[3] = "0";
		 options[4] = "-S";
		 options[5] = "1";
		 RandomForest tree = new RandomForest();         // new instance of tree
		 try {
			tree.setOptions(options);     // set the options
			tree.buildClassifier(dataSet);
			Evaluation eval = new Evaluation(dataSet);
			eval.crossValidateModel(tree, dataSet, 5, new Random(1));
			result = tree.toString();
			result += eval.toSummaryString();
			result += eval.toClassDetailsString();
			result += eval.toMatrixString();
			System.out.println(eval.toMatrixString());
			System.out.println(genTable(eval,"RandomForest"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		 return result;
		 
	}
	
	public String j48graft(Instances dataSet) {
		 
		String result="";
		 String[] options = new String[1];
		 options[0] = "-U";            // unpruned tree
		 J48graft tree = new J48graft();         // new instance of tree
		 try {
			tree.setOptions(options);     // set the options
			tree.buildClassifier(dataSet);
			Evaluation eval = new Evaluation(dataSet);
			eval.crossValidateModel(tree, dataSet, 5, new Random(1));
			result = tree.toString();
			result += eval.toSummaryString();
			result += eval.toClassDetailsString();
			result += eval.toMatrixString();
			System.out.println(eval.toMatrixString());
			System.out.println(genTable(eval,"J48graft"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		 return result;
	}
	
	public String decisionTable(Instances dataSet) {
		 
		String result="";
		 String[] options = new String[2];
		 options[0] = "-F";
		 options[1] = "3";
		
		 
		 DecisionTable dt = new DecisionTable();         // new instance of tree
		 try {
			 
			dt.setOptions(options);     // set the options
			dt.buildClassifier(dataSet);
			Evaluation eval = new Evaluation(dataSet);
			eval.crossValidateModel(dt, dataSet, 5, new Random(1));
			result = dt.toString();
			result += eval.toSummaryString();
			result += eval.toClassDetailsString();
			result += eval.toMatrixString();
			System.out.println(eval.toMatrixString());
			System.out.println(genTable(eval,"DecisionTable"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		 return result;
	}
	
	public String jrip(Instances dataSet) {
		 
		String result="";
		 String[] options = new String[8];
		 options[0] = "-F";
		 options[1] = "3";
		 options[2] = "-N";
		 options[3] = "2.0";
		 options[4] = "-S";
		 options[5] = "1";
		 options[6] = "-O";
		 options[7] = "2";
		 Instances sampledData = null;
		 JRip dt = new JRip();         // new instance of tree
		 try {
			 
			dt.setOptions(options);     // set the options
			System.out.println(dataSet.numInstances());
			if (dataSet.numInstances()>10000) {
				double z = (10000/dataSet.numInstances())*100; 
				String filteroptions="-S 1 -B 1.0 -Z 3";
				Resample sampler = new Resample();
				sampler.setInputFormat(dataSet);
				sampler.setOptions(weka.core.Utils.splitOptions(filteroptions));
				sampler.setRandomSeed((int)System.currentTimeMillis());
				sampledData = dataSet;
				sampledData = Resample.useFilter(sampledData, sampler);
				String filename2 = currentFile.replace(".arff", "T.arff");
				this.saveToArff(sampledData, filename2);
			} else {
				sampledData = dataSet;
			}
			dt.buildClassifier(sampledData);
			Evaluation eval = new Evaluation(sampledData);
			eval.crossValidateModel(dt, dataSet, 5, new Random(1));
			result = dt.toString();
			result += eval.toSummaryString();
			result += eval.toClassDetailsString();
			result += eval.toMatrixString();
			System.out.println(eval.toMatrixString());
			System.out.println(genTable(eval,"Jrip"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		 return result;
	}
	
	public String naiveBayes(Instances data) {
		 
		 String result="";
		 try {
			 
			 NaiveBayesUpdateable nb = new NaiveBayesUpdateable();
			 nb.buildClassifier(data);
			 
			 Evaluation eval = new Evaluation(data);
			 eval.crossValidateModel(nb, data, 5, new Random(1));
			 
			 result = nb.toString();
			 result += eval.toSummaryString();
			 result += eval.toClassDetailsString();
			 result += eval.toMatrixString();
			 System.out.println(eval.toMatrixString());
			 System.out.println(genTable(eval,"NaiveBayes"));
			 
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		 return result;
	}
	

	
	public void classifyAll(String filename) {
		// TODO Auto-generated method stub
        try {
        	String filename2 = filename.replace(".arff", "Filtered.arff");
        	String content= "";
        	Instances data = this.readArff(filename);
			System.out.println("File loaded: " + filename);
			data = this.filterMMS(data);
			System.out.println("File filtered: " + filename);
			if (this.saveToArff(data, filename2)) {
			   System.out.println("Done and saved in: " + filename2);
			}
			String filename3 = filename.replace(".arff", ".log");
			
			content += "Num Attributes: "+data.numAttributes() + "\n";
			content += "Num Instances: "+data.numInstances() + "\n";
			System.out.println("NaiveBayes:");
			content += this.naiveBayes(data);
			System.out.println("NaiveBayes DONE");
			System.out.println("DecisionTable");
			content += this.decisionTable(data);
			System.out.println("DecisionTable DONE");
			System.out.println("JRip");
			content += this.jrip(data);
			System.out.println("JRip DONE");
			System.out.println("RandomForest");
			content += this.randomForest(data);
			System.out.println("RandomForest DONE");
			System.out.println("RandomTree");
			content += this.randomTree(data);
			System.out.println("RandomTree DONE");
			System.out.println("J48graft");
			content += this.j48graft(data);
			System.out.println("J48graft DONE");
			content += "Classification done and logged";
			System.out.println("Classification done and logged");
			this.addToLog(filename3, content);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void addToLog(String filename,String content) {
		try {
			
            FileWriter aWriter = new FileWriter(
					filename, true);
			
			aWriter.write(content);
			aWriter.flush();
			aWriter.close();
			// fh.close();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}
